package com.example.academic_affairs_system.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.academic_affairs_system.common.R;
import com.example.academic_affairs_system.common.UserAuthorizationInfo;
import com.example.academic_affairs_system.exception.SystemException;
import com.example.academic_affairs_system.mapper.AasUserMapper;
import com.example.academic_affairs_system.model.bo.AasClassUserBO;
import com.example.academic_affairs_system.model.dto.user.*;
import com.example.academic_affairs_system.model.entity.AasClass;
import com.example.academic_affairs_system.model.entity.AasClassStudent;
import com.example.academic_affairs_system.model.entity.AasUser;
import com.example.academic_affairs_system.model.vo.LoginResponseVO;
import com.example.academic_affairs_system.service.*;
import com.example.academic_affairs_system.utils.CommonUtils;
import com.example.academic_affairs_system.utils.NanoIdUtils;
import com.example.academic_affairs_system.utils.PasswordSecurity;
import com.example.academic_affairs_system.utils.TokenUtils;
import com.github.benmanes.caffeine.cache.Cache;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 教务系统用户表 服务实现类
 *
 * @author hjx
 * @since 2023-01-31
 */
@Service
public class AasUserServiceImpl extends ServiceImpl<AasUserMapper, AasUser>
    implements IAasUserService {
  @Resource HttpServletResponse response;
  @Resource HttpServletRequest request;
  @Resource IAasClassService iAasClassService;
  @Resource IAasClassStudentService iAasClassStudentService;
  @Resource IAasRoleService iAasRoleService;
  @Resource IAasUserRoleService iAasUserRoleService;

  @Resource(name = "userAuthorizationCache")
  Cache<UserAuthorizationInfo, Long> userAuthorizationCache;

  @Override
  public void login(LoginDTO dto) {
    String username = dto.getUsername();
    String password = dto.getPassword();
    AasUser aasUser =
        baseMapper.selectOne(Wrappers.<AasUser>lambdaQuery().eq(AasUser::getUsername, username));
    if (aasUser == null) {
      throw new SystemException(-999, "用户不存在", "请确保输入的用户名正确");
    } else if (!aasUser.getPassword().equals(PasswordSecurity.generatedPassword(password))) {
      throw new SystemException(-999, "密码错误", "请确保输入的密码正确");
    }
    AasClassUserBO classUserBO = iAasClassService.getClassUserByUserId(aasUser.getId());
    if (classUserBO == null) {
      // 不是负责人也不是学生，用户异常
      throw new SystemException(-999, "用户信息异常", "用户信息异常，请联系负责人");
    }
    loginSuccessHandler(aasUser, classUserBO);
  }

  @Override
  public int register(RegisterDTO dto) {
    AasUser aasUser =
        baseMapper.selectOne(
            Wrappers.<AasUser>lambdaQuery().eq(AasUser::getUsername, dto.getUsername()));
    if (aasUser != null) {
      throw new SystemException(-999, "用户已被注册", "用户已被注册，请登录");
    }
    AasUser register = dto.buildRegister();
    int insert = baseMapper.insert(register);
    // 追加用户权限
    if (insert > 0) {
      long teacherRoleId = iAasRoleService.getTeacherRoleId();
      iAasUserRoleService.addRelated(register.getId(), teacherRoleId);
    }

    return insert;
  }

  @Override
  public String resetPassword(RestPasswordDTO dto) {
    if (!PhoneUtil.isMobile(dto.getConcat())) {
      throw new SystemException(-999, "请输入正确的电话号码", null);
    }
    if (!IdcardUtil.isValidCard(dto.getIdCard())) {
      throw new SystemException(-999, "请输入正确的身份证", null);
    }
    AasUser aasUser =
        baseMapper.selectOne(
            Wrappers.<AasUser>lambdaQuery().eq(AasUser::getIdCard, dto.getIdCard()));
    if (aasUser == null) {
      throw new SystemException(-999, "用户不存在", "请确保已经登记过用户信息");
    }
    if (!dto.getConcat().equals(aasUser.getConcat())
        || !dto.getIdCard().equals(aasUser.getIdCard())) {
      throw new SystemException(-999, "用户信息不正确", "请确保用户的基本信息正确");
    }
    // 生成随机密码
    String pwd = NanoIdUtils.randomNaoId(8);
    aasUser.setPassword(PasswordSecurity.generatedPassword(pwd));
    // 重置密码成功就进行用户登出
    if (baseMapper.updateById(aasUser) > 0) {
      logout();
    }
    return pwd;
  }

  @Override
  public int editPassword(EditPasswordDTO dto) {
    if (!dto.getPwd().equals(dto.getPwd2())) {
      throw new SystemException(-999, "修改信息不一致", "请确保修改密码和确认密码一致");
    }
    LoginResponseVO loginResponseVO = TokenUtils.parseToken(request);
    AasUser aasUser = baseMapper.selectById(loginResponseVO.getUserId());
    aasUser.setPassword(PasswordSecurity.generatedPassword(dto.getPwd()));
    // 修改密码成功就进行用户登出
    int i = baseMapper.updateById(aasUser);
    if (i > 0) {
      logout();
    }
    return i;
  }

  @Override
  public void logout() {
    String token = request.getHeader(HttpHeaders.AUTHORIZATION);
    if (StrUtil.isNotBlank(token)) {
      // 获取用户信息
      LoginResponseVO loginResponseVO = TokenUtils.parseToken(token);
      // 清除用户的令牌
      clearTokenByUserId(loginResponseVO.getUserId());
    }
  }

  @Override
  @Transactional(rollbackFor = {Exception.class})
  public String addStudent(HandlerStudentDTO dto) {
    validateHandlerPower(dto);
    // 获取班级代码
    AasClass byId = iAasClassService.getById(dto.getClassId());
    Long classCode = byId.getClassCode();
    List<AasClassStudent> classStudentByClassId =
        iAasClassStudentService.getClassStudentByClassId(dto.getClassId());
    String studentNumber;
    if (CollectionUtil.isEmpty(classStudentByClassId)) {
      studentNumber = buildStudentNumber(classCode, 0);
    } else {
      studentNumber = buildStudentNumber(classCode, classStudentByClassId.size());
    }
    // 加入用户数据表
    AasUser student = dto.buildGenerateStudent(studentNumber);
    baseMapper.insert(student);
    // 将用户添加至学生班级表
    iAasClassStudentService.addStudentToClass(dto.buildClassStudent(student, dto.getClassId()));
    // 追加权限关联
    long studentRoleId = iAasRoleService.getStudentRoleId();
    iAasUserRoleService.addRelated(student.getId(), studentRoleId);
    // 返回学号
    return studentNumber;
  }

  @Override
  public int edit(HandlerStudentDTO dto) {
    validateHandlerPower(dto);
    int result = 0;
    // 更新至学生班级表
    if (dto.editStudentClass()) {
      AasClassStudent classStudent =
          iAasClassStudentService.getClassStudentByStudentId(dto.getStudentId());
      classStudent.setPosition(dto.getPosition());
      classStudent.setPositionDesc(dto.getPositionDesc());
      classStudent.setRating(dto.getRating());
      // 更换班级
      if (dto.getClassId() != null) {
        classStudent.setClassId(dto.getClassId());
      }
      result += iAasClassStudentService.getBaseMapper().updateById(classStudent);
    }
    if (dto.editStudentUser()) {
      AasUser student = dto.buildUserStudent(false);
      student.setId(dto.getStudentId());
      result += baseMapper.updateById(student);
    }
    return result;
  }

  @Override
  public int delete(long userId) {
    return baseMapper.deleteById(userId);
  }

  /**
   * 登录成功追加响应信息
   *
   * <p>将数据进行整合，并生成令牌更新至缓存
   *
   * @param aasUser 查询的用户信息
   * @param classUserBO 查询的用户在班级的信息
   */
  private void loginSuccessHandler(AasUser aasUser, AasClassUserBO classUserBO) {
    // 生成令牌并加入缓存中
    String token = refreshToken(aasUser);
    response.setHeader(HttpHeaders.AUTHORIZATION, token);
    // 返回用户的班级信息、职位信息
    CommonUtils.writePojo(
        response,
        R.ok(
            LoginResponseVO.builder()
                .userId(aasUser.getId())
                .nickname(aasUser.getNickname())
                .position(classUserBO.getPosition())
                .classname(classUserBO.getClassname())
                .loginTimestamp(System.currentTimeMillis())
                .lastTime(DateUtil.format(aasUser.getLastTime(), "yyyy-MM-dd HH:mm:ss"))
                .build()));
    // 更新最后的登录时间
    aasUser.setLastTime(LocalDateTimeUtil.now());
    baseMapper.updateById(aasUser);
  }

  /**
   * 根据用户 id 清除令牌
   *
   * @param userId 用户 id
   */
  private void clearTokenByUserId(Long userId) {
    // 清除用户的令牌
    for (Map.Entry<UserAuthorizationInfo, Long> entry : userAuthorizationCache.asMap().entrySet()) {
      UserAuthorizationInfo key = entry.getKey();
      if (key.getUserId().equals(userId)) {
        // 缓存中存在相同的用户令牌 - 进行清除
        userAuthorizationCache.invalidate(key);
        break;
      }
    }
  }

  /**
   * 刷新用户令牌
   *
   * <p>删除缓存中的用户令牌并重新生成
   *
   * @param aasUser 用户的信息
   * @return {@link String} 生成的令牌
   */
  private String refreshToken(AasUser aasUser) {
    // 清除用户的令牌
    clearTokenByUserId(aasUser.getId());
    // 生成令牌并加入缓存中
    LoginResponseVO loginResponseVO =
        LoginResponseVO.builder()
            .userId(aasUser.getId())
            .nickname(aasUser.getNickname())
            .loginTimestamp(System.currentTimeMillis())
            .build();
    String token = TokenUtils.generateToken(loginResponseVO);
    // 有效期为 1 天
    userAuthorizationCache.put(
        UserAuthorizationInfo.builder().userId(aasUser.getId()).token(token).build(),
        System.currentTimeMillis() / 1000 + 86400);
    return token;
  }

  /**
   * 构建学生学号
   *
   * @param classCode 班级编号
   * @param classStudentSize 班级的学生数量
   * @return 返回学号
   */
  private String buildStudentNumber(Long classCode, int classStudentSize) {
    String studentCode = String.valueOf(classCode);
    int currentSize = classStudentSize + 1;
    if (currentSize < 10) {
      studentCode += "00" + currentSize;
    } else if (currentSize < 100) {
      studentCode += "0" + currentSize;
    } else {
      studentCode += currentSize;
    }
    return studentCode;
  }

  /**
   * 校验操作权限
   *
   * @param dto 操作用户信息
   * @return {@link AasClass} 返回当前负责人的班级信息
   */
  private void validateHandlerPower(HandlerStudentDTO dto) {
    LoginResponseVO loginResponseVO = TokenUtils.parseToken(request);
    Long userId = loginResponseVO.getUserId();
    // 必须是班级负责人才可以在班级中添加学生
    List<AasClass> classByDirectorsId = iAasClassService.getClassByDirectorsId(userId);
    if (classByDirectorsId == null || CollectionUtil.isEmpty(classByDirectorsId)) {
      throw new SystemException(-999, "无法进行添加操作", "您还没有负责任何班级");
    } else if (!classByDirectorsId.stream()
        .map(AasClass::getId)
        .collect(Collectors.toList())
        .contains(dto.getClassId())) {
      throw new SystemException(-999, "操作失败", "您不是该班级的负责人");
    }
  }
}
